package com.example.springboot.redis.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@RestController
public class TestController {

	@Autowired
	private RedisTemplate<String, String> redisTemplate;

	/**
	 * ab测试： ab -c100 -n300 http://localhost:8080/redis
	 * 阻塞命令，测试redis池子是否好用
	 */
	@GetMapping("/redis")
	public String redis() {
		String pop = redisTemplate.opsForList().leftPop("list1", 5, TimeUnit.SECONDS);
		return pop;
	}


	/**
	 * ab测试： ab -c3 -n10 http://localhost:8080/redis_lock
	 * 阻塞命令，测试redis池子是否好用
	 */
	@GetMapping("/redis_lock")
	public String redis_lock() {
		return getDataFromDbWithRedisLock();
	}


	/**
	 * redis分布式锁
	 *
	 * @return
	 */
	public String getDataFromDbWithRedisLock() {
		String key = "redis_lock_test";
		String uuid = UUID.randomUUID().toString();
		//redis分布式锁，如果key不存在设置成功，设置过期时间1分钟
		Boolean lock = redisTemplate.opsForValue().setIfAbsent(key, uuid, 1, TimeUnit.MINUTES);
		String currentThreadName = Thread.currentThread().getName();
		if (lock) {
			System.out.println(currentThreadName + "获取lock成功，uuid：" + uuid);
			//加锁成功，查询数据库
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			String dbData = "db data --- abc";
			System.out.println(currentThreadName + "获取数据成功，dbData：" + dbData);

			String redisUuid = redisTemplate.opsForValue().get(key).toString();
			System.out.println(currentThreadName + "redisUuid：" + uuid);

			//通过lua脚本，保证获取和删除原子性
			DefaultRedisScript<Long> script = new DefaultRedisScript<>();
			script.setResultType(Long.class);
			script.setScriptSource(new ResourceScriptSource(new ClassPathResource("TestRedisLock.lua")));

			Long execute = redisTemplate.execute(script, Arrays.asList(key), redisUuid);
			System.out.println(currentThreadName + "lua解锁结果：" + execute.intValue());
			if (BigDecimal.ZERO.longValue() != execute.longValue()) {
				return dbData;
			}
		}

		//没有分布式锁,等待1秒后,继续自旋
		try {
			System.out.println(currentThreadName + "没有获取到锁，继续自旋");
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//自旋
		return getDataFromDbWithRedisLock();
	}
}
